package com.wlyy.bcwlw.sys;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import com.wlyy.bcwlw.common.context.SysVersion;
import com.wlyy.bcwlw.common.exception.AuthenticationFailedException;
import com.wlyy.bcwlw.common.utils.CookieUtils;
import com.wlyy.bcwlw.common.utils.SecurityUtil;
import com.wlyy.bcwlw.sys.user.entity.UserDTO;
import com.wlyy.bcwlw.sys.user.entity.UserProfile;
import com.wlyy.bcwlw.sys.user.service.UserManageService;

/**
 * 
 * @ClassName. AppSecurityFilter
 * @Description. 系统安全过滤器
 * @author zhangyang
 * @date 2015年8月31日 下午3:03:06
 * @version V1.0
 */
public class AppSecurityFilter extends OncePerRequestFilter{
	private static final Logger logger = LogManager.getLogger(AppSecurityFilter.class);

	private static String APP_ENCODING = "_app_encoding_tag_";
	private static final int COOKIE_MAXAGE_ONE_YEAR = 365 * 24 * 60 * 60;
	
	private String runMode;
	 
	public void setRunMode(String runMode) {
		this.runMode = runMode;
	}
	
	/**
	 * 
	 */
	public AppSecurityFilter() {
		super();
	}
	
	/**
	 * 
	* <p>对所有路径进行拦截 </p> 
	* @param request
	* @param response
	* @param filterChain
	* @throws ServletException
	* @throws IOException
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		/**
		 * 安全认证
		 */
		try {
			//获取登录时的用户名
			String url = request.getRequestURI();
			String username = "";
			if(StringUtils.contains(url, SecurityConst.getDefaultLoginDoFormUrlKey()) ) {
				username =request.getParameter("userName");
			}
			
			// 获取回调
			UserProfile up = null;
			if( StringUtils.isNotBlank(username) ) {
				try {
					up = processCasLogin(request, response, username);
				} catch (AuthenticationFailedException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			} else {
				up = processUserprofile(request, response);
			}
			// 如果需要验证，而且有没有验证通过那么抛异常
			boolean protectUrl = isProctectUrl(up, getRealUrl(request));
			//为不受保护路径 登录过 或验证信息仍存在
			
			/**
			 * 过滤条件：
			 * dwr 和 在公开路径列表里 不被过滤
			 * 已退出用户的请求将被过滤
			 * 配置为菜单url 且 权限不足的将被过滤
			 * 没有配置菜单且不为公开的将不被过滤
			 * 
			 */
			if (protectUrl && (!isLogin(up,getRealUrl(request)) || !authenticate(request))) {
				
				//判断session过期，返回sessionTimeout
				logger.error((up == null? "" : up.getUserName()) + "访问" + request.getServletPath() + "受限");
				// 不再抛出异常，转向无权访问的提示页面 091010
                response.sendRedirect(request.getContextPath() + "/forbidden.htm");
                return;
				//throw new AuthenticationFailedException("无权访问");
			}
			//当前服务器时间，页面可以直接获取${CURRENT_DATE}
			request.setAttribute("VERSION", SysVersion.get());
			request.setAttribute("CURRENT_DATE", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
			request.setAttribute("CURRENT_TIME", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
			
			String tag = request.getParameter(APP_ENCODING);
			if (StringUtils.isBlank(tag)
					|| "POST".equalsIgnoreCase(request.getMethod())) {
				filterChain.doFilter(request, response);
			}
			
			
			setUserOpraterCookie(response, request);
		} finally {
			SecurityContextHolder.clearContext();
		}
	
		
	}
	
	/**
	 * 
	* @Title. getRealUrl
	* @Description. 解析路径
	* @param request
	* @return String
	* @exception.
	 */
	private String getRealUrl(HttpServletRequest request){
		String requestURL = request.getRequestURI();
		String contextURL = request.getContextPath();
		if(requestURL.startsWith(contextURL)){
			return requestURL.substring(contextURL.length(), requestURL.length());
		}
		return "";
	}
	
	/**
	 * 
	* @Title. isProctectUrl
	* @Description. 是否为受保护路径 过滤dwr和不受保护路径
	* @param up
	* @param url
	* @return boolean
	* @exception.
	 */
	private boolean isProctectUrl(UserProfile up, String url) {
		return !url.startsWith("/dwr") && getSecurityService().isProctectUrl(url);
	}

	/**
	 * 
	* @Title. authenticate
	* @Description. 路径权限验证
	* @param request
	* @return boolean
	* @exception.
	 */
	private boolean authenticate(HttpServletRequest request) {
		//没有配置菜单url的不被过滤
		if ("debug".equals(runMode)) {
			return true;
		}
		//其他按权限分
		boolean result = false;
		String uri = getRealUrl(request);
		if (logger.isDebugEnabled()) {
			logger.debug("检查权限开始:" + new Timestamp(System.currentTimeMillis()).toString());
		}
		// 检查用户是否有访问当前uri的权限 有返回true
		result = getSecurityService().checkPermission(uri);
		if (logger.isDebugEnabled()) {
			logger.debug("检查权限结束:"
					+ new Timestamp(System.currentTimeMillis()).toString());
		}
		return result;
	}
	
	/**
	 * 
	* @Title. isLogin
	* @Description. 是否登录
	* @param up
	* @param url
	* @return boolean
	* @exception.
	 */
	private boolean isLogin(UserProfile up, String url) {
		if (up == null || up.isAnonymous()) {
			return false;
		}
		return true;
	}

	/**
	 * 
	* @Title. processCasLogin
	* @Description. 处理登录信息
	* @param request
	* @param response
	* @param username
	* @return
	* @throws AuthenticationFailedException
	* @throws IllegalAccessException
	* @throws InvocationTargetException UserProfile
	* @exception.
	 */
	private UserProfile processCasLogin(HttpServletRequest request,
			HttpServletResponse response, String username)
			throws AuthenticationFailedException, IllegalAccessException,
			InvocationTargetException {
		UserProfile up;
		up = SecuritySessionContext.getUser(request.getSession());
		UserDTO userDTO = getUserManageService().findByUsername(username);
		String userId = userDTO==null?null:userDTO.getUserId();
		
		if (null == up || up.getUserId() == null 
				|| SecurityConst.ANONYMOUS_ID.equals(up.getUserId())
				|| ( userId != null && !userId.equals(up.getUserId()) )
				) {
			
			if (StringUtils.isNotEmpty(userId) && !SecurityConst.ANONYMOUS_ID.equals(userId)) {
				try {
					up = getSecurityService().getUserProfile(userId, SecurityUtil.getClientIP(request));
				} catch (AuthenticationFailedException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
			
		}
		if (null == up || up.getUserId() == null) {
			up = UserProfile.ANONYMOUS;
			up.setIp(SecurityUtil.getClientIP(request));
		} 
		
		SecurityContextHolder.setLoginContext(request, response, up);
		return up;
	}
	
	private UserManageService getUserManageService() {
		return (UserManageService) WebApplicationContextUtils.getWebApplicationContext( getServletContext() ).getBean("userManageService");
	}
	
	/**
	 * 
	* @Title. processUserprofile
	* @Description. 处理用户信息 用户名为空时 （记住）
	* @param request
	* @param response
	* @return UserProfile
	* @exception.
	 */
	private UserProfile processUserprofile(HttpServletRequest request, HttpServletResponse response){
		UserProfile userP;
		userP = SecuritySessionContext.getUser(request.getSession());
		//保证两个用户id一致才可以让用户登录
		String userId = SecurityContextHolder.getUserDataFromCookie(request,SecurityConst.AQ_AUTHENTICATION_COOKIE_KEY);
		//签名后的用户id
		String userIdSign = SecurityUtil.decodeCookieUserId(SecurityContextHolder.getUserDataFromCookie(request,SecurityConst.AQ_AUTHENTICATION_COOKIE_KEY_SIGN));
		
		if (null == userP || userP.getUserId() == null 
				|| SecurityConst.ANONYMOUS_ID.equals(userP.getUserId())
				|| ( userId != null && !userId.equals(userP.getUserId()) )
				) {
			
			if (StringUtils.isNotEmpty(userId) && !SecurityConst.ANONYMOUS_ID.equals(userId) && StringUtils.equals(userIdSign, userId)) {
				try {
					userP = getSecurityService().getUserProfile(userId, SecurityUtil.getClientIP(request));
				} catch (AuthenticationFailedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}
		if (null == userP || userP.getUserId() == null) {
			userP = UserProfile.ANONYMOUS;
			userP.setIp(SecurityUtil.getClientIP(request));
		} 
		SecurityContextHolder.setLoginContextWithoutCookie(request, response, userP);
		return userP;
	}
	
	private SecurityService getSecurityService() {
		
		return (SecurityService) WebApplicationContextUtils.getWebApplicationContext( getServletContext() ).getBean("securityService");
	}
	
	/**
	 * 
	* @Title. setUserOpraterCookie
	* @Description. 用来设置用户与服务器最后一次交互的时间
	* @param response
	* @param request void
	* @exception.
	 */
	private void setUserOpraterCookie(HttpServletResponse response, HttpServletRequest request) {
		String path = request.getPathInfo();
		String uri = request.getRequestURI();
		if (StringUtils.isEmpty(uri)) {
			return;
		}
				
		if (path != null && (path.indexOf("AutooutIdletimeController.checkIdleTime.dwr") == -1 
				&& path.indexOf("UserController.getCurrUserId") == -1
				&& path.indexOf("/call/plaincall/ApplicationController.getUserApplications") == -1)
				&& uri.indexOf("/aq/main/initDeskMsgs.do") == -1) {
			 CookieUtils.setCookie(response, SecurityConst.USER_OPERATER_COOKIE_KEY, String.valueOf(System.currentTimeMillis()), COOKIE_MAXAGE_ONE_YEAR);
		}
	   
	}
	
	
}
